package com.zzz.springsecuritystudy.config;

import com.zzz.springsecuritystudy.annotation.AnonymousAccess;
import com.zzz.springsecuritystudy.filter.TokenConfigurer;
import com.zzz.springsecuritystudy.filter.TokenFilter;
import com.zzz.springsecuritystudy.security.*;
import com.zzz.springsecuritystudy.service.UserDetailsServiceImpl;
import com.zzz.springsecuritystudy.util.JwtUtils;
import com.zzz.springsecuritystudy.util.StringUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.filter.CorsFilter;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import java.util.*;

/**
 * @author zhuzhizun
 * @date 2021/9/15
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)// 控制@Secured @prePostEnabled权限注解
@RequiredArgsConstructor
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    // RequestMappingHandlerMapping  获取上下文
    private final ApplicationContext applicationContext;
    // 跨域
    private final CorsFilter corsFilter;
    // 访问不带凭证  401
    private final EntryPointHandler entryPointHandler;
    // 匿名访问受保护资源  403
    private final RestAccessDeniedHandler restAccessDeniedHandler;


    // 得到匿名标记的方法  可以匿名访问
    private Map<String, Set<String>> getAnonymousUrl(Map<RequestMappingInfo, HandlerMethod> handlerMethodMap) {
        Map<String, Set<String>> anonymousUrls = new HashMap<>(1);
        Set<String> anony = new HashSet<>();
        for (Map.Entry<RequestMappingInfo, HandlerMethod> infoEntry : handlerMethodMap.entrySet()) {
            HandlerMethod handlerMethod = infoEntry.getValue();
            AnonymousAccess anonymousAccess = handlerMethod.getMethodAnnotation(AnonymousAccess.class);
            if (null != anonymousAccess) {
                List<RequestMethod> requestMethods = new ArrayList<>(infoEntry.getKey().getMethodsCondition().getMethods());
                String name = requestMethods.get(0).name();
                if (StringUtils.isNotEmpty(name)) {
                    anony.addAll(infoEntry.getKey().getPatternsCondition().getPatterns());
                }
            }
        }
        anonymousUrls.put("anony", anony);
        return anonymousUrls;

    }


    @Override
    protected void configure(HttpSecurity http) throws Exception {

        // 搜寻匿名标记 url： @AnonymousAccess
        RequestMappingHandlerMapping requestMappingHandlerMapping = (RequestMappingHandlerMapping) applicationContext.getBean("requestMappingHandlerMapping");
        Map<RequestMappingInfo, HandlerMethod> handlerMethodMap = requestMappingHandlerMapping.getHandlerMethods();

        // 获取匿名标记
        Map<String, Set<String>> anonymousUrls = getAnonymousUrl(handlerMethodMap);
        http
                // 禁用 CSRF
                .csrf().disable()
                // 跨域Filter
                .addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class)
                // 授权异常
                .exceptionHandling()
                .authenticationEntryPoint(entryPointHandler)
                .accessDeniedHandler(restAccessDeniedHandler)
                // 防止iframe 造成跨域
                .and()
                .headers()
                .frameOptions()
                .disable()
                // 不创建会话
                .and()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                // 静态资源等等
                .antMatchers(
                        HttpMethod.GET,
                        "/*.html",
                        "/**/*.html",
                        "/**/*.css",
                        "/**/*.js"
                ).permitAll()
                // 放行OPTIONS请求
                .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
                // 自定义匿名访问所有url放行：允许匿名和带Token访问
                .antMatchers(anonymousUrls.get("anony").toArray(new String[0])).permitAll()
                // 所有请求都需要认证
                .anyRequest().authenticated()
                .and().apply(securityConfigurerAdapter());

    }
    // tokenFilter
    private TokenConfigurer securityConfigurerAdapter() {
        return new TokenConfigurer();
    }
}